package com.example.im.mvp.presenter

import android.app.Activity
import android.app.ProgressDialog
import android.content.Context
import android.database.Cursor
import android.media.MediaPlayer
import android.net.Uri
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import android.view.View
import com.example.im.common.Constants
import com.example.im.common.ImApplication
import com.example.im.entity.ChatContent
import com.example.im.entity.ChatRecordEntire
import com.example.im.http.APIRetrofit
import com.example.im.http.RetrofitManager
import com.example.im.mvp.base.BasePresenter
import com.example.im.mvp.view.IChatView
import com.example.im.utils.JsonUtils
import com.example.im.utils.LogUtils
import com.example.im.xmpp.IMConnection
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.BiFunction
import io.reactivex.schedulers.Schedulers
import okhttp3.MediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
import org.csource.fastdfs.ClientGlobal
import org.csource.fastdfs.StorageClient
import org.csource.fastdfs.TrackerClient
import org.jivesoftware.smack.packet.Message
import top.zibin.luban.Luban
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.net.URI
import java.util.*
import java.util.concurrent.TimeUnit

open class ChatPresenter(
    mContext: Context, mView: IChatView?
) :
    BasePresenter<IChatView>(mContext, mView) {

    private var userName: String? = null
    private var messageType: Message.Type? = null

    private var imConnection: IMConnection? = null

    init {
        imConnection = IMConnection.instance!!
    }

    fun setInitUserAndType(userName: String, messageType: Message.Type) {
        this.userName = userName
        this.messageType = messageType
    }

    fun chatApplyPermissions(activity: Activity) {
        val observable = this.applyPermissions(activity)
            .subscribe {
                getView().isPermissionsPresenter(it)
                if (!it) getView()
                    .onHint("该应用需要访问部分权限，请到 “应用信息 -> 权限” 中授予！")
            }
        this.disposable.add(observable)
    }

    /**
     * 数据整合
     * @param file 文件
     * */
    private fun setMessageMerge(file: File): ChatContent {
        val type = when (file.name.substring(file.name.lastIndexOf(".") + 1)) {
            "png" -> Constants.CONTENT_TYPE_PHOTO
            "jpg" -> Constants.CONTENT_TYPE_PHOTO
            "mp4" -> Constants.CONTENT_TYPE_VIDEO
            "amr" -> Constants.CONTENT_TYPE_VOICE
            else -> Constants.CONTENT_TYPE_FILE
        }
        val body = when (type) {
            Constants.CONTENT_TYPE_PHOTO -> "[图片]"
            Constants.CONTENT_TYPE_VIDEO -> "[视频]"
            Constants.CONTENT_TYPE_VOICE -> "[语音]"
            else -> "[文件]"
        }

        val duration = if (type == Constants.CONTENT_TYPE_VOICE) {
            val fis = FileInputStream(file)
            val mediaPlayer = MediaPlayer()
            mediaPlayer.setDataSource(fis.fd)
            mediaPlayer.prepare()
            mediaPlayer.duration
        } else 0

        val content = with(ChatContent(), {
            this.body = body
            this.type = type
            this.fileInfo = with(ChatContent.FileInfo(), {
                this.name = file.name
                this.length = file.length().toInt()
                this.duration = duration
                this.localUrl = file.path
                getFileInfo()
            })
            getChatContent()
        })
        return setMessageMerge(content)
    }

    private fun setMessageMerge(contentText: String, itelUser: String?): ChatContent {
        val content = with(ChatContent(), {
            this.body = contentText
            this.itel = itelUser
            this.type = Constants.CONTENT_TYPE_TEXT
            getChatContent()
        })
        return setMessageMerge(content)
    }

    private fun setMessageMerge(content: ChatContent): ChatContent {
        //保存数据
        val entire = imConnection!!.setMessageStorage(
            userName!!, with(Message(), {
                this.body = content.toString()
                this.type = messageType
                this
            }),
            Constants.CHAT_SEND, Constants.CHAT_STATUS_2
        )
        return with(content, {
            this.entireId = entire.id
            this.getChatContent()
        })
    }

    /**
     * 发送文本内容
     * */
    private fun chatSendText(contentObservable: Observable<ChatContent>) {
        val observable = contentObservable.observeOn(Schedulers.io()).map {
            AbstractMap.SimpleEntry<ChatContent, Boolean>(it, true)
        }
        this.subscribe(observable)
    }

    /**
     *发送文本内容
     * */
    private fun chatSendText(content: ChatContent) {
        val contentObservable = Observable.just(content)
            .map {
                setMessageMerge(it)
            }
        this.chatSendText(contentObservable)
    }

    /**
     *发送文本内容
     * */
    fun chatSendText(text: String, itelUser: HashSet<String>) {
        if (text.isEmpty())
            return
        val contentObservable = if (itelUser.isEmpty()) {
            Observable.just(text).map {
                setMessageMerge(it, null)
            }
        } else {
            Observable.zip(
                Observable.just(text),
                Observable.just(itelUser)
                    .map { JsonUtils.objectToString(it) },
                BiFunction<String, String?, ChatContent> { t1, t2 ->
                    setMessageMerge(t1, t2)
                })
        }
        this.chatSendText(contentObservable)
    }

    /**
     * 发送文件内容（语音、图片、视频、文件）
     * */
    private fun chatSendFile(contentsObservable: Observable<List<ChatContent>>) {

        val observable = contentsObservable.observeOn(Schedulers.io()).flatMap {
            Observable.fromIterable(it)
        }.flatMap({
            if (it.type == Constants.CONTENT_TYPE_PHOTO) {
                Observable.just(it).map { c ->
                    Luban.with(getContext()).load(c.fileInfo!!.localUrl)
                        .ignoreBy(100).get(c.fileInfo!!.localUrl)
                }
                    .flatMap { file ->
                        val requestBody = RequestBody.create(
                            MediaType.parse("multipart/form-data"), file
                        )
                        val part = MultipartBody.Part.createFormData(
                            "file_${System.currentTimeMillis()}",
                            file.name, requestBody
                        )
                        RetrofitManager.retrofit().create(APIRetrofit::class.java)
                            .upLoadFile(part).map { image ->
                                if (image.isRet)
                                    "http://${Constants.SERVICE_IP}:4869/${image.info!!.md5}"
                                else ""
                            }
                    }
                    .onErrorReturn { "" }
            } else {
                Observable.just(it.fileInfo)
                    .map { file ->
                        val suffix = file.name!!.substring(file.name!!.lastIndexOf(".") + 1)
                        val fileSuffix = if (file.name!!.length == suffix.length)
                            suffix else null
                        getStorageClient().upload_file(file.localUrl, fileSuffix, null)
                            ?: arrayOf()
                    }
                    .map { array ->
                        if (array.isNotEmpty())
                            JsonUtils.objectToString(array)
                        else ""
                    }
                    .onErrorReturn { "" }
            }
        }, { t1, t2 ->
            val content = with(t1, {
                this.fileInfo!!.netUrl =
                    if (t2.isNotEmpty()) t2 else null
                this.getChatContent()
            })
            AbstractMap.SimpleEntry<ChatContent, Boolean>(content, t2.isNotEmpty())
        })
        this.subscribe(observable)
    }

    private fun chatSendFile(content: ChatContent) {
        val contentsObservable = Observable.just(content)
            .map {
                setMessageMerge(it)
            }
            .toList().toObservable()
        this.chatSendFile(contentsObservable)
    }

    fun chatSendFile(paths: List<String>) {
        val contentsObservable = Observable.fromIterable(paths)
            .map { File(it) }
            .map { setMessageMerge(it) }
            .toList().toObservable()
        this.chatSendFile(contentsObservable)
    }

    /**
     * 消息重发
     * */
    fun chatSendRepeat(entire: ChatRecordEntire) {
        val content = JsonUtils.stringToObject(
            entire.body, ChatContent::class.java
        )
        if (content.type == Constants.CONTENT_TYPE_TEXT) {
            this.chatSendText(content)
        } else {
            this.chatSendFile(content)
        }
    }

    /**
     * 消息转发
     * */
    fun chatSendRelay(content: ChatContent) {
        content.entireId = 0
        this.chatSendText(content)
        getView().onHint("已转发")
    }

    private fun subscribe(observable: Observable<AbstractMap.SimpleEntry<ChatContent, Boolean>>) {
        val subscribe = observable.observeOn(Schedulers.io()).flatMap({
            Observable.just(it).delay(100, TimeUnit.MILLISECONDS)
                .map { map ->
                    if (map.value) imConnection!!.sendChatMessage(
                        userName!!, map.key.toString(), messageType!!
                    ) else false
                }.onErrorReturn { false }
        }, { t1, t2 ->
            AbstractMap.SimpleEntry<ChatContent, Any>(t1.key, t2)
        })
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({

                if (it.value is Boolean && it.value as Boolean) {
                    LogUtils.e(JsonUtils.objectToString(it.key))
                } else {
                    imConnection!!.setMessageStorage(
                        userName!!, with(Message(), {
                            this.body = it.key.toString()
                            this.type = messageType
                            this
                        }), Constants.CHAT_SEND, Constants.CHAT_STATUS_3
                    )
                    if (it.value is String) {
                        getView().onHint(it.value.toString())
                    }
                }
            }, {
                it.printStackTrace()
                if (isViewAttached)
                    getView().onHint("发送异常${it.message}")
            })
        this.disposable.add(subscribe)
    }

    private fun getStorageClient(): StorageClient {
        ClientGlobal.init("fdfs_client.conf")
        val tracker = TrackerClient()
        val trackerServer = tracker.connection
        return StorageClient(trackerServer, null)
    }

    /**
     * 语音接收播放
     * */
    fun chatReceiveVoice(view: View, entire: ChatRecordEntire) {

        val chatRecordEntire = ImApplication.appDatabase!!.chatRecordEntireDao()
            .getChatRecordEntireByID(entire.id)
        val observable = Observable.just(chatRecordEntire).observeOn(Schedulers.io())
            .map { it.body }.filter { it.isNotEmpty() }
            .map {
                JsonUtils.stringToObject(it, ChatContent::class.java)
            }
            .flatMap {
                if (it.fileInfo!!.localUrl.isNullOrEmpty()) {

                    val array = JsonUtils.stringToObject(it.fileInfo!!.netUrl, List::class.java)

                    val bytes = getStorageClient().download_file(
                        array[0].toString(),
                        array[1].toString()
                    )

                    val fileName = array[1].toString().substring(
                        array[1].toString().lastIndexOf("/") + 1,
                        array[1].toString().length
                    )
                    val file = File(
                        Environment.getExternalStorageDirectory(), fileName
                    )
                    if (!file.exists()) file.createNewFile()

                    val fis = FileOutputStream(file)
                    fis.write(bytes)

                    it.fileInfo!!.localUrl = file.path
                    ImApplication.appDatabase!!.chatRecordEntireDao().upDateChatRecordEntireBody(
                        JsonUtils.objectToString(it),
                        chatRecordEntire.id
                    )

                    Observable.just(file.path)
                } else {
                    Observable.just(it.fileInfo!!.localUrl)
                }
            }
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                getView().onVoicePathPresenter(view, it)
            }, {
                getView().onHint("播放错误：${it.message}}")
            })
        this.disposable.add(observable)
    }


    /**
     * 下载文件
     * */
    fun chatDownloadFiles(entire: ChatRecordEntire) {

        val dialog = ProgressDialog(getContext())
        dialog.setCanceledOnTouchOutside(false)
        dialog.setMessage("请稍候...")
        val observable = Observable.just(entire).observeOn(Schedulers.io())
            .map { it.body }.filter { it.isNotEmpty() }
            .map {
                JsonUtils.stringToObject(it, ChatContent::class.java)
            }
            .flatMap {
                if (it.fileInfo!!.localUrl.isNullOrEmpty()) {

                    val array = JsonUtils.stringToObject(it.fileInfo!!.netUrl, List::class.java)

                    val bytes = getStorageClient().download_file(
                        array[0].toString(),
                        array[1].toString()
                    )

                    val file = File(
                        Environment.getExternalStorageDirectory(), it.fileInfo!!.name
                    )
                    if (!file.exists()) file.createNewFile()

                    val fis = FileOutputStream(file)
                    fis.write(bytes)

                    it.fileInfo!!.localUrl = file.path
                    ImApplication.appDatabase!!.chatRecordEntireDao().upDateChatRecordEntireBody(
                        JsonUtils.objectToString(it),
                        entire.id
                    )

                    Observable.just(file.path)
                } else {
                    Observable.just(it.fileInfo!!.localUrl)
                }
            }
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                getView().onHint("下载完成：$it")
            }, {
                getView().onHint("下载错误：${it.message}}")
                dialog.cancel()
            },
                { dialog.cancel() },
                { dialog.show() }
            )
        this.disposable.add(observable)
    }
}